home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d1 / multicol.arc / MC.C < prev    next >
C/C++ Source or Header  |  1988-06-19  |  11KB  |  465 lines

  1. /*
  2.  *             m c . c
  3.  *
  4.  * Multi-column filter
  5.  *
  6.  */
  7.  
  8. /*)BUILD
  9.     $(TKBOPTIONS) = {
  10.         TASK    =    ...MCX
  11.         UNITS    =    10
  12.         ACTFIL    =    10
  13.     }
  14. */
  15.  
  16. /*
  17. )EDITLEVEL=40
  18.  * Edit history
  19.  * 0.0 ??-???-?? ???    Original implementation distributed with DECUS C.
  20.  * 1.0 19-May-81 JSL    Extensive reorganization; added -t option, balancing
  21.  *            of columns on final page.  Bugfix:  Don't put out a
  22.  *            blank initial item.
  23.  * 2.0 20-Jul-82 JSL    Converted to tool standards.
  24.  * 2.1 23-Jul-82 JSL    Added -g switch.
  25.  * 2.2 27-Jul-82 JSL    Added multiple-file handling.
  26.  * 2.3  1-Aug-82 JSL    Redid overstrike handling; much more extensive error
  27.  *            and bounds checks; debug conditionally compiled.
  28.  * 2.4 ??-Aug-82  MM    Change default page height to 60
  29.  * 2.5 23-Sep-82 JSL    Added conditional code for VAX-11 C
  30.  * 2.6 11-apr-85 SCP    Ported to ms-DOS DESMET 'C' removed piping 
  31.  *          capability for performance, removed space between opt and #
  32.  */
  33.  
  34. char    *documentation[] = {
  35. "    mc  [-ccolumns] [-hheight] [-ggutter] [-wwidth]",
  36. "        [filespec | aliasspec]...   outfile",
  37. "",
  38. "mc reads one or more files and converts them to a single multi-column file",
  39. "that it writes to an output file.    ",
  40. "",
  41. "Alias specifications provide a method for controlling the placement of file",
  42. "data.  An alias specification is a reference to another file (or alias)",
  43. "specification.  File and alias specifications are numbered, starting at one",
  44. "for the left-most such specification; switches and their arguments do not",
  45. "affect the numbering.  The alias specification #n indicates that the n'th",
  46. "specification is to be repeated.  Such a specification is legal only if it",
  47. "refers to an earlier specification.",
  48. "",
  49. "mc has two modes of operation:",
  50. "   one input file  -   the file is presented in multicolumn format,",
  51. "                        records may be truncated to fit.",
  52. "   multiple inputs -   each input file (or alais) will occupy one column ",
  53. "                         on the output.  Files should be of equal length.",
  54. "",
  55. "The following switches are available:",
  56. " -c    Number of columns                             default - 2",
  57. " -h    Height, in lines, of a page                   default - 58",
  58. " -g    Gutter width (the space between columns)      default - 1",
  59. " -w    Width, in characters, of a page               default - 80",
  60. "",
  61. "    mc  [-c2] [-h58] [-g1] [-w80]",
  62. "        [filespec | aliasspec]...   outfile",
  63. 0 };
  64.  
  65. #ifdef vax11c
  66. #include ctype.h
  67. #include stdio.h
  68. #define exits(x)    exit(x)
  69. #define IO_ERROR    2
  70. #define FALSE        0
  71. #define TRUE        1
  72. #define EOS        0
  73. #else
  74. #include <stdio.h>
  75. #endif
  76.  
  77. /*
  78.  * Turn on to include debugging code
  79.  */
  80. /* #define DEBUG */
  81.  
  82. #define    LINEMAX    256            /* Maximum line length handled    */
  83.                     /*  (also maximum column width)    */
  84. #define    NFILES    10            /* Maximum files (including    */
  85.                     /*  aliased files)        */
  86. #define    ALIAS    '#'            /* Marks an alias argument    */
  87.                     /*  (Can't be "-")        */
  88. #ifdef DEBUG
  89. int    debug    = 0;
  90. #endif
  91.  
  92. int        NEWLN = 3338;
  93.  
  94. int    columns    = -1;            /* All these will be given    */
  95. int    gutter    = -1;            /*  default values later unless    */
  96. int    height    = -1;            /*  the user sets them first    */
  97. int    width    = -1;            /*  (to a positive value!)    */
  98.  
  99. int    pause    = FALSE;        /* Pause-at-end of page flag    */
  100. int    first    = TRUE;            /* First-time-through flag    */
  101. int    cwidth;                /* Total (column+gutter) width    */
  102. int    pagesize;            /* Total bytes in page        */
  103. int    nf;                /* Number of file & alias specs    */
  104. int    files    = 0;            /* Number of files still open    */
  105. int    aliases    = 0;            /* Number of alias specs    */
  106.  
  107. FILE    file[NFILES];            /* File pointers for our files    */
  108. FILE    outfp;            /* SP - and rightmost is output file */
  109. char    line[LINEMAX];            /* Input line buffer        */
  110. int    linelen;            /* Length of a line in line[]    */
  111. int    lineend;            /* Last usable line[] position    */
  112. char    *page;                /* -> page paste-up matrix    */
  113.  
  114. main(argc,argv)
  115. int argc;
  116. char *argv[];
  117. {    register char *p;
  118.     register int c,i;
  119.     int n;
  120.     long    PAGE;
  121.     FILE fp;
  122.  
  123.     if (argc < 2)    usage();
  124.     if (argc == 2 && argv[1][0] == '?' && strlen(argv[1]) == 1)
  125.     {    help();
  126.         return;
  127.     }
  128.     --argc;        /* SP---*/
  129.     nf = argc - 1;
  130.     for (i = 1; i < argc; i++) {
  131.         if (argv[i][0] != '-')        break;
  132.         --nf;    
  133.         c = argv[i][1];
  134.                 switch(tolower(c))
  135.                 {
  136. #ifdef DEBUG
  137.                 case 'd':            debug++;        break;
  138. #endif
  139.                 case 'c':
  140.                     columns = atoi(&argv[i][2]);
  141.                     if (columns <= 0)            bad("Columns");
  142.                     break;
  143.  
  144.                 case 'g':
  145.                     gutter = atoi(&argv[i][2]);
  146.                     if (gutter <= 0)        bad("Gutter");
  147.                     break;
  148.  
  149.                 case 'h':
  150.                     height = atoi(&argv[i][2]);
  151.                     if (height <= 0)            bad("Height");
  152.                     break;
  153.  
  154.                 case 'w':
  155.                     width = atoi(&argv[i][2]);
  156.                     if (width <= 0)                bad("Width");
  157.                     break;
  158.  
  159.                 default:
  160.                     usage();
  161.                     break;
  162.                 }
  163.         
  164.     }
  165.  
  166.  
  167.     if (nf == 0)
  168.     {    
  169.         error("Too few files");
  170.     }
  171.     else
  172.         for (  ; i < argc ; i++)
  173.             if (p = argv[i])
  174.                 switch (*p)
  175.                 {
  176.                 case ALIAS:
  177.                     n = atoi(&p[1]) - 1;
  178.                     if (n < 0 || n >= files)    usage("File");
  179.                     file[files++] = file[n];
  180.                     aliases++;
  181.                     break;
  182.  
  183.                 default:
  184.                     if ((fp = fopen(p,"r")) == NULL)
  185.                     {    fprintf(stderr,
  186.                             "%s: ",p);
  187.                         error("Can't open");
  188.                     }
  189. #ifdef DEBUG
  190. fprintf(stderr,"\nOpened input:   %s",p);
  191. #endif
  192.                     file[files++] = fp;
  193.                     break;
  194.                 }
  195.     if (nf > NFILES )
  196.         error("Too many file and alias arguments, or bad parm order");
  197.  
  198.     if ((outfp = fopen(argv[argc],"w")) == NULL)
  199.         {        fprintf(stderr,    "%s: ",argv[i]);
  200.                 error("Can't open");        }
  201. #ifdef DEBUG
  202. fprintf(stderr,"\nOpened output:   %s",argv[i]);
  203. #endif
  204.     files -= aliases;            /* Aliases aren't open    */
  205.  
  206. /*
  207.  * Establish defaults for any parameters the user didn't set
  208.  */
  209.     if (width < 0)        width = 80;
  210.     if (gutter < 0)        gutter = 1;
  211.     if (height < 0)        height = 58;
  212.     if (columns < 0)
  213.         if (nf > 1)
  214.             columns = nf;
  215.         else
  216.             columns = 2;
  217.  
  218. /*
  219.  * The last column isn't followed by a gutter, but dealing with this makes
  220.  * the computation too complex; so we simply pretend the page is wider, which
  221.  * is ok since the code trims the trailing spaces that would go there anyway.
  222.  * This is, of course, quite wasteful of space, but then so is the whole algo-
  223.  * rithm; we shouldn't be storing ANY of the gutters explicitly.
  224.  */
  225.     width += gutter;
  226.     cwidth = width/columns;
  227.  
  228.     if (cwidth <= gutter || (cwidth - gutter) > LINEMAX)
  229.         error("Unreasonable -c/-g/-w combination\n");
  230.  
  231.     lineend = line + (cwidth - gutter);
  232.  
  233.     PAGE = ( long) height * (width + gutter);
  234.     if (PAGE > 30000L)
  235.         error("Insufficient memory - sorry\n");
  236.     page = calloc(pagesize = (int) PAGE);
  237.     if (page == NULL )
  238.         error("Insufficient memory - sorry\n");
  239.  
  240. #ifdef DEBUG
  241.     if (debug)
  242.     {    fprintf(stderr,"width %d, height %d, columns %d, cwidth %d\n",
  243.             width,height,columns,cwidth);
  244.         fprintf(stderr,
  245.             "\tgutter %d, pause %d, pagesize %d, page at 0%o\n",
  246.             gutter,pause,pagesize,page);
  247.         fprintf(stderr,"%d files(%d real + %d aliases)\n",
  248.             nf,files,aliases);    }
  249. #endif
  250.  
  251.     process();
  252.     fclose(outfp);
  253.     free(page);
  254. }
  255.  
  256. /*
  257.  * Process all the data
  258.  */
  259. process()
  260. {    register int    offset;            /* Offset into page    */
  261.     register int    items;            /* Counts items added    */
  262.     register int    maxitems;        /* Room for this many    */
  263.     int curfile;                /* Current file        */
  264.  
  265.     maxitems = columns * height;
  266.     fputc('\f',outfp);
  267.     blank();
  268.     curfile = items = offset = 0;
  269.     while (get(file[curfile]))
  270.     {    if (items >= maxitems)
  271.         {    output(items);
  272.             blank();
  273.             items = offset = 0;
  274.         }
  275. #ifdef DEBUG
  276.         if (debug > 3)
  277.             fprintf(stderr,"Inserting %s at offset %d, file %d\n",
  278.                 line,offset,curfile);
  279. #endif
  280.         strncpy(page+offset,line,linelen);
  281.         items++;
  282.         if ((items % height) == 0)    /* Bottom of a column    */
  283.         {    curfile = (curfile + 1) % nf;
  284. #ifdef DEBUG
  285.             if (debug > 1)
  286.                 fprintf(stderr,"Switching to file %d of %d\n",
  287.                     curfile,files);
  288. #endif
  289.         }
  290.         offset += cwidth;
  291.     }
  292.     output(items);
  293. }
  294.  
  295. /*
  296.  * Print out the buffered page, which has been filled with items items.
  297.  */
  298. output(items)
  299. int items;                /* # of items the caller used    */
  300. {    int nrows;
  301.     register int i,col,row;
  302.  
  303. #ifdef DEBUG
  304.     if (debug)        fprintf(stderr,"output items(%d), nf(%d)\n",items,nf);
  305. #endif
  306.     if (items <= 0)            /* Nothin' to do        */
  307.         return;
  308.  
  309. /*
  310.  * Get number of rows we'll need.  This is the basis of the "last page"
  311.  * optimization - we don't use all the rows, just enough to hold everything
  312.  * (items/columns, rounded up).  If there's more than are one file, just use
  313.  * the whole page.
  314.  */
  315.     if (nf == 1)        nrows = (items + (columns - 1)) / columns;
  316.         else        nrows = height;
  317.  
  318. #ifdef DEBUG
  319.     if (debug > 1)
  320.         fprintf(stderr,"items %d, nrows %d\n",items,nrows);
  321.     if (debug > 2)
  322.     {    page[pagesize] = 0;
  323.         fprintf(stderr,"Dump of page:\n%s\n",page);
  324.     }
  325. #endif
  326.  
  327.     if (first)
  328.         first = FALSE;
  329.     else
  330.     {    if (pause)
  331.         {    printf("\t\t\t      Type CTRL/Z to exit, any other key to continue...");
  332.             fflush(stdout);
  333.             i = ci();
  334.             fwrite(&NEWLN,1,2,outfp);
  335.             if (i == 26)        /* CTRL/Z        */
  336.                 exit();
  337.         }
  338.         fputc('\f',outfp);
  339.     }
  340.  
  341. /*
  342.  * Scan through page[] row-wise, after having filled it column-wise.  (Page[]
  343.  * is laid out column-wise in memory.)
  344.  */
  345.     for (row = 0; row < nrows; row++)
  346.     {    for (col = 0; col < columns; col++)
  347.             putitem(page+(row+col*nrows)*cwidth,
  348.                 (col == columns - 1));
  349.         fwrite(&NEWLN,1,2,outfp);
  350.     }
  351. }
  352.  
  353. /*
  354.  * Put out one item, possibly trimming trailing spaces
  355.  */
  356. putitem(base,trim)
  357. register char *base;                /* First char to put    */
  358. int trim;                    /* Trim trailing spaces    */
  359. {    register char *end;            /* End of item        */
  360.     int    len;
  361.  
  362.     end = &base[cwidth - 1];
  363.     if (trim)
  364.         while (*end == ' ')
  365.             --end;
  366.     len = end - base;
  367. #ifdef DEBUG
  368.     if (len > 256)
  369.         puts("\n wow -excessive length");        
  370. #endif
  371.     if (len  > 0)
  372.         fwrite(base,1,len + 1,outfp);
  373. }
  374.  
  375. /* * Blank out page[] */
  376.  
  377. blank()
  378. {    register int n;
  379.  
  380.     for (n = 0; n < pagesize;)
  381.         page[n++] = ' ';
  382. }
  383.  
  384. /* * Fill line[]; return FALSE when all files have reached EOF, TRUE until then.
  385.  */
  386. get(fp)
  387. FILE  fp;
  388. {    register char *p;            /* Current char pos    */
  389.     register char *high;            /* Char pos high water    */
  390.     register int c;                /* Character        */
  391.     char tempc;
  392.  
  393.     if ((tempc = getc(fp)) == -1)
  394.     {    linelen = 0;            /* Pretend we read ""    */
  395.         return(TRUE);
  396.     }
  397.     ungetc(tempc,fp);
  398.  
  399.     high = p = line;
  400.     while ((c = getc(fp)) != EOF && c != '\n')
  401.         switch(c)
  402.         {
  403.         case '\b':
  404.             if (p > line)
  405.                 --p;
  406.             break;
  407.  
  408.         case '\r':
  409.             p = line;
  410.             break;
  411.  
  412.         case '\t':
  413.             if (((p - line) & 07) != 07)
  414.                 ungetc(c,fp);
  415.             c = ' ';
  416.         /*
  417.          * Fall through...
  418.          */
  419.         default:
  420.             if (isprint(c))
  421.             {    if ((p < lineend)
  422.                  && (p == high || *p == '_' || *p == ' '))
  423.                 {    *p = c;
  424.                     if (p == high)
  425.                         high++;
  426.                 }
  427.                 p++;
  428.                 }
  429.             break;
  430.         }
  431.  
  432.     linelen = high - line;
  433.  
  434.      if (c != EOF)
  435.          return(TRUE);
  436.      else
  437.          return((--files != 0));
  438. }
  439.  
  440. bad(s)
  441. char *s;
  442. {    printf("\n %s specification bad",s);    usage();    }
  443.  
  444. usage()
  445. {
  446.     fprintf(stderr,"\nUsage:\n  mc  [-ccolumns] [-ggutter] ");
  447.         puts("[-hheight] [-wwidth] [infile(s) | #n]...  outfile\n");
  448.     error("\n   mc ?    for more help");
  449. }
  450.  
  451. help()
  452. /*
  453.  * Give good help
  454.  */
  455. {    register char    **dp;
  456.  
  457.     for (dp = documentation; *dp; dp++)
  458.         printf("%s\n",*dp);
  459. }
  460.  
  461. error(str)
  462. char    *str;
  463. {    puts(str);
  464.     exit(1);
  465. }